<?php

class DB {

    private static $_conns = array();

    private $_dbh;

    /**
     * db 构造函数
     *
     * @param string $dsn
     * @param string $user
     * @param string $password
     * @param string $charset
     * @param string $failover
     * @param boolean $persistent
     * @param integer $timeout
     */
    private function __construct($dsn, $user, $password, $charset, $failover = '', $persistent = false, $timeout = 0, $setNames = true) {

        $attr = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                      PDO::ATTR_PERSISTENT => $persistent);
        if (0 < $timeout) {
            $attr[PDO::ATTR_TIMEOUT] = $timeout;
        }

        try {
            $this->_dbh = new PDO($dsn, $user, $password, $attr);
            if ($setNames) {
                $this->_dbh->exec("SET NAMES '" . $charset . "'");
            }
        } catch (PDOException $e){
            //Event::run('systemDBFailedOnce', $dsn);
            if ($failover) {
                try {
                    $this->_dbh = new PDO($failover, $user, $password, $attr);
                    if ($setNames) {
                        $this->_dbh->exec("SET NAMES '" . $charset . "'");
                    }
                } catch (PDOException $e){
                    //Event::run('systemDBFailedTwice', $dsn);
                    throw new Yaf_Exception("can't connect to the server because:" . $e->getMessage());
                }
            } else {

                throw new Yaf_Exception("can't connect to the server because:" . $e->getMessage());
            }
        }
    }

    /**
     * 获取数据库连接类
     *
     * @param string $dsn
     * @param string $user
     * @param string $password
     * @param string $charset
     * @param string $failover
     * @param boolean $persistent
     * @param integer $timeout
     * @return DB 实例
     */
    public static function connect($dsn, $user, $password, $charset, $failover = '', $persistent = false, $timeout = 0, $setNames = true) {

        $tryConn = false;
        if (self::$_conns[$dsn] && PHP_SAPI == 'cli') {
            $info = self::$_conns[$dsn]->getAttr(PDO::ATTR_SERVER_INFO);
            if ($info == 'MySQL server has gone away') {
                $tryConn = true;
            }
        }

        if (!isset(self::$_conns[$dsn]) || $tryConn){
            //self::_benchmark(__FUNCTION__, array('dsn' => $dsn));
            self::$_conns[$dsn] = new DB($dsn, $user, $password, $charset, $failover, $persistent, $timeout, $setNames);
        }

        return self::$_conns[$dsn];
    }

    /**
     * 开启事物
     */
    public function begin() {

        $this->_dbh->beginTransaction();
    }

    /**
     * 提交事务
     */
    public function commit() {

        $this->_dbh->commit();
    }

    /**
     * 回滚事务
     */
    public function rollBack() {

        $this->_dbh->rollBack();
    }

    /**
     * 取得记录的第一行
     *
     * @param sql string $query
     * @param array $params
     */
    public function fetchRow($query, $params = array()) {

        //self::_benchmark($query, $params);
        $stmt = $this->_dbh->prepare($query);
        $stmt->execute($params);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row;

    }

    /**
     * 取得所有的记录
     *
     * @param sql string $query
     * @param array $params
     * @return array
     */
    public function fetchAll($query, $params = array()) {

        //self::_benchmark($query, $params);
        $stmt = $this->_dbh->prepare($query);
        $stmt->execute($params);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
        return $rows;
    }

    /**
     * 获取记录的第一行第一列
     *
     * @param string sql $query
     * @param array $params
     */
    public function fetchOne($query, $params = array()) {

        //self::_benchmark($query, $params);
        $stmt = $this->_dbh->prepare($query);
        $result = $stmt->execute($params);
        if ($result) {
            $row = $stmt->fetchColumn();
        }
        return $row ;
    }

    /**
     * 执行sql 语句
     *
     * @param sqlstring $query
     * @param array $params
     * @return 更新的记录的条数
     */

    public function exec($query, $params = array()) {

        //self::_benchmark($query, $params);
        $stmt = $this->_dbh->prepare($query);
        return $stmt->execute($params);
    }

    /**
     * 获取最后一条记录的id
     *
     * @author 黄磊 <huanglei@comsenz.com>
     * @date 10/21/2009
     * @return string
     */
    public function lastInsertId() {

        return $this->_dbh->lastInsertId();
    }

    /**
     * 关闭数据库连接
     * @param string $dsn
     */
    public function close($dsn = null) {

        if ($dsn) {
            //self::_benchmark(__FUNCTION__, array('dsn' => $dsn));
            self::$_conns[$dsn]->close();
            unset(self::$_conns[$dsn]);
        } else {
            $this->_dbh = NULL;
        }
    }

    /**
     * closeAll
     * 关闭所有的链接
     *
     * @return true
     */
    public static function closeAll() {

        if (self::$_conns) {
            //self::_benchmark(__FUNCTION__);
            foreach (self::$_conns as $key => $con) {
                self::$_conns[$key]->close();
                unset(self::$_conns[$key]);
            }
        }

        return true;
    }

    /**
     * getAttr
     * 获取连接属性
     *
     * @return mixed
     */
    public function getAttr($attr) {

        return $this->_dbh->getAttribute($attr);
    }

    /**
     * 用于显示页面底部显示查询
     *
     * @param string 方法名称
     * @param array  参数
     * @return void
     */
    private static function _benchmark($sql, $args = null) {

        $options = Bootstrap::factory()->getOption('profiler');
        if (Bootstrap_Env::get('profiler') && in_array('pdo', $options)) {
            $pdoRequests = Bootstrap_Env::get('profile_pdo');
            if (!$pdoRequests) {
                $pdoRequests = array();
            }
            $pdoRequests[][$sql][] = $args;
            Bootstrap_Env::set('profile_pdo', $pdoRequests);
        }

    }

}
